#pragma once
#include <iostream>
#include <cstdio>
#include <string>
#include <filesystem>
#include <sstream>
#include <unistd.h>
#include <memory>
#include <ctime>
#include <sys/syscall.h>
#include <pthread.h>
#include <fstream>
#include "Mutex.hpp"

using namespace MutexModule;

namespace LogModule
{
	const std::string gsep = "\r\n";

	// 基类
	class LogStrategy
	{
	public:
		virtual void Synclog(const std::string &message) = 0;
		~LogStrategy() = default;
	};

	// 控制台日志策略
	class ConsoleLog : public LogStrategy
	{
	public:
		void Synclog(const std::string &message) override
		{
			// 上锁
			MutexGuard guard(_mutex);
			std::cout << message << gsep;
		}
		~ConsoleLog() {}

	private:
		Mutex _mutex; // 互斥锁
	};

	const std::string DefaultPath = "./log";
	const std::string DefaultFileName = "mylog.log";
	// 文件日志策略
	class FileLog : public LogStrategy
	{
	public:
		FileLog(const std::string &path = DefaultPath, const std::string &filename = DefaultFileName)
			: _path(path), _filename(filename)
		{
			if (std::filesystem::exists(_path))
			{
				return;
			}
			try
			{
				std::filesystem::create_directories(_path);
			}
			catch (const std::filesystem::filesystem_error &e)
			{
				std::cerr << e.what() << '\n';
			}
		}

		void Synclog(const std::string &message) override
		{
			MutexGuard guard(_mutex);
			std::string fullname = _path + (_path.back() == '/' ? "" : "/") + _filename;

			// 打开文件
			std::ofstream file(fullname, std::ios::app);
			if (!file.is_open())
			{
				std::cerr << "无法打开日志文件：" << fullname << std::endl;
				return;
			}

			// 写入日志
			file << message << gsep;

			file.close();
		}

		~FileLog() {}

	private:
		Mutex _mutex; // 互斥锁
		std::string _path;
		std::string _filename;
	};

	enum class LogLevel
	{
		DEBUG,
		INFO,
		WARNING,
		ERROR,
		FATAL
	};

	const std::string ChangeStr(LogLevel level)
	{
		switch (level)
		{
		case LogLevel::DEBUG:
			return "DEBUG";
		case LogLevel::INFO:
			return "INFO";
		case LogLevel::WARNING:
			return "WARNING";
		case LogLevel::ERROR:
			return "ERROR";
		case LogLevel::FATAL:
			return "FATAL";
		default:
			return "UNKNOWN";
		}
	}

	std::string GetCurrentTime()
	{
		time_t now = time(nullptr);

		struct tm tm_info;
		localtime_r(&now, &tm_info);

		char buffer[128];
		snprintf(buffer, sizeof(buffer), "%04d-%02d-%02d %02d:%02d:%02d",
				 tm_info.tm_year + 1900,
				 tm_info.tm_mon + 1,
				 tm_info.tm_mday,
				 tm_info.tm_hour,
				 tm_info.tm_min,
				 tm_info.tm_sec);

		return std::string(buffer);
	}

	class Logger
	{
	public:
		Logger(std::unique_ptr<LogStrategy> logStrategy = std::make_unique<ConsoleLog>()) : _logStrategy(std::move(logStrategy)) {}

		void UseConsole()
		{
			_logStrategy = std::make_unique<ConsoleLog>();
		}

		void UseFile()
		{
			_logStrategy = std::make_unique<FileLog>();
		}

		class LogMessage
		{
		public:
			LogMessage(Logger &logger, LogLevel level, const std::string &file, int line)
				: _logger(logger), _level(level), _file(file), _line(line)
			{
				std::ostringstream _oss;
				_oss << "[" << GetCurrentTime() << "] "
					 << "[" << ChangeStr(_level) << "] "
					 << "[pid: " << getpid() << "] "
					 << "[tid: " << syscall(SYS_gettid) << "] "
					 << "[" << _file << "] "
					 << "[" << _line << "] "
					 << "- ";

				_loginfo = _oss.str();
			}

			template <class T>
			LogMessage &operator<<(const T &info)
			{
				std::stringstream ss;
				ss << info;
				_loginfo += ss.str();
				return *this;
			}

			~LogMessage()
			{
				if (_logger._logStrategy)
				{
					_logger._logStrategy->Synclog(_loginfo);
				}
			}

		private:
			Logger &_logger;
			LogLevel _level;
			std::string _file;
			int _line;
			std::string _loginfo;
		};

		LogMessage operator()(LogLevel level, const std::string &file, int line)
		{
			return LogMessage(*this, level, file, line);
		}

	private:
		std::unique_ptr<LogStrategy> _logStrategy;
	};

	Logger logger;

	#define LOG(level) LogModule::logger(level, __FILE__, __LINE__)
	#define Enable_Console() logger.UseConsole()
	#define Enable_File() logger.UseFile()
}